
package com.scs.application.modules.order.service.impl;

import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.model.CommonResult;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.thread.BatchSaveTask;
import com.scs.application.core.thread.DefaultSaveHandler;
import com.scs.application.core.utils.*;
import com.scs.application.enums.BasePrintBusType;
import com.scs.application.modules.base.entity.HospitalConfig;
import com.scs.application.modules.base.entity.HospitalMaterial;
import com.scs.application.modules.base.entity.HospitalWarehouse;
import com.scs.application.modules.base.entity.Supplier;
import com.scs.application.modules.base.excel.handler.ExcelImportUtilCustom;
import com.scs.application.modules.base.request.HospitalMaterialRequest;
import com.scs.application.modules.base.request.HospitalSupplierRequest;
import com.scs.application.modules.base.request.PrintTemplateQueryRequest;
import com.scs.application.modules.base.service.*;
import com.scs.application.modules.base.vo.HospitalMaterialVO;
import com.scs.application.modules.base.vo.HospitalSupplierVO;
import com.scs.application.modules.base.vo.PrintTemplateVO;
import com.scs.application.modules.msg.service.PushedRecordService;
import com.scs.application.modules.order.consts.DictConsts;
import com.scs.application.modules.order.consts.MatrTypeCodeConsts;
import com.scs.application.modules.order.dto.CloseDeliveryDTO;
import com.scs.application.modules.order.dto.DeliveryDTO;
import com.scs.application.modules.order.dto.DeliveryItemDTO;
import com.scs.application.modules.order.dto.ExcelDeliveryDTO;
import com.scs.application.modules.order.entity.*;
import com.scs.application.modules.order.mapper.DeliveryItemSnMapper;
import com.scs.application.modules.order.mapper.DeliveryMapper;
import com.scs.application.modules.order.mapper.PurchaseOrderMapper;
import com.scs.application.modules.order.request.DeliveryItemQueryRequest;
import com.scs.application.modules.order.request.DeliveryQueryRequest;
import com.scs.application.modules.order.service.*;
import com.scs.application.modules.order.vo.DeliveryItemVO;
import com.scs.application.modules.order.vo.DeliveryVO;
import com.scs.application.modules.order.vo.ExcelDeliveryVO;
import com.scs.application.modules.thirdparty.erp.request.DeliveryRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 发货单 service 实现
 *
 */
@Slf4j
@AllArgsConstructor
@Service
@Transactional(rollbackFor = Exception.class)
public class DeliveryServiceImpl extends BaseServiceImpl<DeliveryMapper, Delivery> implements DeliveryService {

    private final DeliveryItemService deliveryItemService;

    private final PurchaseOrderItemService purchaseOrderItemService;

    private final DeliveryItemSnMapper deliveryItemSnMapper;

    private final PurchaseOrderMapper purchaseOrderMapper;

    private final DeliveryItemSnService deliveryItemSnService;

    private final PurchaseProcessService purchaseProcessService;

    private final HospitalSupplierService hospitalSupplierService;

    private final HospitalMaterialService hospitalMaterialService;

    private final HospitalWarehouseService hospitalWarehouseService;

    private final SupplierService supplierService;

    private final PrintTemplateService printTemplateService;

    private final HospitalConfigService hospitalConfigService;

    private final ICreateDelivery createDelivery;
    private final DeliveryERPImpl deliveryErp;
    private final PushedRecordService pushedRecordService;

    @Override
    public boolean removeCascadeById(Serializable id) {
        deliveryItemService.remove(Wrappers.<DeliveryItem>query().eq("delivery_id", id));
        return super.removeById(id);
    }

    @Override
    public List<Delivery> list(DeliveryQueryRequest request) {
        List<Delivery> list = this.list(Wrappers.<Delivery>query()
                .eq("supplier_id", UserUtils.getSupplierId())
                .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                .eq(StringUtils.isNotBlank(String.valueOf(request.getIsManual())), "is_manual", request.getIsManual())
                .eq(StringUtils.isNotBlank(String.valueOf(request.getIsClosed())), "is_closed", request.getIsClosed())
                .like(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .like(StringUtils.isNotBlank(request.getPoCode()), "po_code", request.getPoCode())
                .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                .orderByDesc("code")
        );
        return list;
    }

    @Override
    public IPage<Delivery> page(DeliveryQueryRequest request) {
        IPage page = this.page(request.getPage(), Wrappers.<Delivery>query()
                .eq("supplier_id", UserUtils.getSupplierId())
                .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                .eq(StringUtils.isNotBlank(String.valueOf(request.getIsManual())), "is_manual", request.getIsManual())
                .eq(StringUtils.isNotBlank(String.valueOf(request.getIsClosed())), "is_closed", request.getIsClosed())
                .like(StringUtils.isNotBlank(request.getCode()), "code", request.getCode())
                .like(StringUtils.isNotBlank(request.getPoCode()), "po_code", request.getPoCode())
                .orderByDesc("code")
        );
        return page;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Delivery delivery(String id) {
        synchronized (id.intern()) {
            // 根据id查出发货单
            Delivery delivery = this.getById(id);
            if (delivery == null) {
                throw new BusinessException("发货单不存在，请刷新重试");
            }
            String status = delivery.getStatus();
            //查询订单是否关闭
            PurchaseOrder p = purchaseOrderMapper.selectById(delivery.getPoId());
            if (p.getIsClosed() == DictConsts.CLOSED_YES) {
                throw new BusinessException("该订单被关闭,无法进行发货");
            }
            //发货单是否已关闭
            if (delivery.getIsClosed() == DictConsts.CLOSED_YES) {
                throw new BusinessException(delivery.getCode() + "该发货单已被关闭,不可发货");
            }
            // 发货单状态!=0
            if (!status.equals(DictConsts.STATUS_DELIVERY_UNFILLED)) {
                throw new BusinessException(delivery.getCode() + " 当前状态不可发货");
            }

            // 根据delivery_id查询发货明细
            List<DeliveryItem> deliveryItemList = deliveryItemService.list(Wrappers.<DeliveryItem>query().eq("delivery_id", id));
            if (deliveryItemList.size() == 0) {
                throw new BusinessException("没有明细,无法发货");
            }
            deliveryItemList.stream().filter(item -> item.getQty() <= 0).findFirst().ifPresent(item -> {
                throw new BusinessException(item.getMaterialName() + "实发数量不能小于等于0");
            });
            deliveryItemList.stream().forEach(item -> {
                BusinessException.throwErrorIf(StringUtils.isBlank(item.getLot()),"批号不能为空,商品{}",item.getMaterialName());
                BusinessException.throwErrorIf(item.getExpiredDate() == null,"有效日期不能为空,商品{}",item.getMaterialName());
                BusinessException.throwErrorIf(item.getProductionDate() == null,"生产日期不能为空,商品{}",item.getMaterialName());
            });

            // 如果是手工发货单
            if (delivery.getIsManual() == DictConsts.STATUS_YES) {
                this.update(delivery, Wrappers.<Delivery>update()
                        .set("status", DictConsts.STATUS_DELIVERY_SENT)
                        .set("gmt_delivery", DateUtils.now())
                        .set("creator", UserUtils.currentUser().getId())
                        .eq("id", id));
                Delivery deliverydb= this.getById(delivery.getId());
                deliverydb.setMsgReturn("发货成功，该订单为手动发货单，已全部发货");
                return deliverydb;
            }

        /*if (GlobalConsts.YES_STR.equalsIgnoreCase(delivery.getIsOneThingOneYard())) {
            // 如果为一物一码，必须先进行打印，否则不允许发货
            for (DeliveryItem item : deliveryItemList) {
                Integer count = deliveryItemSnMapper.selectCount(Wrappers.<DeliveryItemSn>query().eq("delivery_item_id", item.getId()));
                if (count == null) {
                    continue;
                }
                if (count.intValue() != item.getQty().intValue()) {
                    throw new BusinessException(MessageFormat.format("{0}-{1} 还未打印SN 或 与实际发货数量不一致，请先进行打印操作", item.getMaterialName(), item.getMaterialSpec()));
                }
            }
        }*/
            //一物一码的或者是需要打印RFID的，生成SN码
            HospitalConfig hospitalConfig = hospitalConfigService.findByHospitalId(delivery.getHospitalId());
            if (GlobalConsts.YES_STR.equalsIgnoreCase(delivery.getIsOneThingOneYard()) || (hospitalConfig != null && GlobalConsts.YES_STR.equals(hospitalConfig.getFlagPrintRfid()))) {
//                deliveryItemSnService.saveDeliveryItemSns(id, null);
                if (hospitalConfig != null && GlobalConsts.YES_STR.equals(hospitalConfig.getFlagDeliveryUdiNecessary())) { //UDI必填，判断是否都填了UDI
                    List<DeliveryItemSn> allSnList = deliveryItemSnService.list(Wrappers.<DeliveryItemSn>query()
                            .eq("delivery_id", id)
                            .orderByDesc("gmt_modified"));
                    Boolean hasEmptyUdit = allSnList.stream().filter(sn -> StringUtils.isEmpty(sn.getUdi())).findAny().isPresent();
                    if (hasEmptyUdit) {
                        throw new BusinessException("您需要填写所有的UDI信息，请点击明细列表的最后一个按钮进行UDI的填写");
                    }
                }
            }

            List<DeliveryItemSn> deliveryItemSnList = deliveryItemSnService.list(Wrappers.<DeliveryItemSn>query().eq("delivery_id", id));
            if (ObjectUtil.length(deliveryItemSnList) > 0) {
                Map<String,List<DeliveryItemSn>> mapByDeliveryItemId = deliveryItemSnList.stream().collect(Collectors.groupingBy(DeliveryItemSn::getDeliveryItemId));
                deliveryItemList.stream().forEach(item -> {
                    if (!mapByDeliveryItemId.containsKey(item.getId())) {
                        BusinessException.throwError("该发货单发货数量发生变化，需要重新生成标签码，请在发货单界面手动执行【清除标签】操作后再进行发货");
                    }
                    if (!NumberUtil.equals(mapByDeliveryItemId.get(item.getId()).size(),item.getQty())) {
                        BusinessException.throwError("该发货单发货数量发生变化，需要重新生成标签码，请在发货单界面手动执行【清除标签】操作后再进行发货");
                    }
                });

            }

            Map<String, List<DeliveryItem>> map = deliveryItemList.stream().collect(Collectors.groupingBy(o ->
                    o.getPoItemId()
            ));
            List<PurchaseOrderItem> poItems = purchaseOrderItemService.list(Wrappers.<PurchaseOrderItem>query()
                    .eq("po_id", delivery.getPoId()));

            for (Map.Entry<String, List<DeliveryItem>> entry : map.entrySet()) {
                List<DeliveryItem> deliveryItems = entry.getValue();
                Double sumQty = deliveryItems.stream().collect(Collectors.summingDouble(DeliveryItem::getQty));
                DeliveryItem item = deliveryItems.get(0);
                // 实发总和>应发
                Double shouldDeliveryQty = item.getShouldDeliveryQty();
                shouldDeliveryQty = deliveryItems.stream().collect(Collectors.summingDouble(DeliveryItem::getShouldDeliveryQty));
                if (sumQty > shouldDeliveryQty) {
                    throw new BusinessException(MessageFormat.format(item.getMaterialName() + "本次实发数量超过可发数量,可发:{0},本次实发:{1}", shouldDeliveryQty, sumQty));
                }
                //根据发货单明细的采购单明细id,查询采购明细
                Optional<PurchaseOrderItem> optOpItem = poItems.stream().filter(poItem -> item.getPoItemId().equals(poItem.getId())).findAny();
                if (!optOpItem.isPresent()) {
                    continue;
                }
                HospitalMaterialRequest hospitalMaterialRequest = new HospitalMaterialRequest();
                hospitalMaterialRequest.setCode(item.getMaterialCode())
                        .setHospitalId(delivery.getHospitalId())
                        .setSupplierId(delivery.getSupplierId());
                HospitalMaterial hospitalMaterial = hospitalMaterialService.getOneHospitalMaterial(hospitalMaterialRequest);
                BusinessException.throwErrorIf(
                        ObjectUtil.isNotNull(hospitalMaterial) && hospitalMaterial.getInactive() != HospitalMaterial.ACTIVATE,
                        "商品已被禁用,无法发货,商品【{}】,规格【{}】,编码【{}】",hospitalMaterial.getMaterialName(),hospitalMaterial.getMaterialSpec(),hospitalMaterial.getMaterialCode()
                );

                PurchaseOrderItem pItem = optOpItem.get();
                PurchaseOrderItem orderItem = new PurchaseOrderItem();
                //如果采购数量=已发货数量+本次发货数量 则采购明细全部发货
                DeliveryItem deliveryItemSum = deliveryItemService.getOne(Wrappers.<DeliveryItem>query().select("sum(qty) as qty").eq("po_item_id", pItem.getId()));
                double sumQtyDb = 0;
                if (deliveryItemSum != null) {
                    sumQtyDb = deliveryItemSum.getQty();
                }
                if (pItem.getQty() <= sumQtyDb) {
                    //修改采购单发货数量  发货金额  发货状态为 2 全部发货
                    orderItem.setDeliveryStatus(DictConsts.STATUS_DELIVERYED)
                            .setDeliveredQty(pItem.getQty())
                            .setDeliveredAmount(item.getSubtotalAmount())
                            .setId(item.getPoItemId());
                    pItem.setDeliveryStatus(DictConsts.STATUS_DELIVERYED);
                    purchaseOrderItemService.updateById(orderItem);
                    // 采购数量 > 已发货数量+本次发货数量 则采购明细部分发货
                } else {
                    //修改采购单发货数量  发货金额  发货状态为 1 部分发货
                    orderItem.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY)
                            .setDeliveredQty(sumQtyDb)
                            .setDeliveredAmount(item.getPrice() * sumQtyDb)
                            .setId(item.getPoItemId());
                    pItem.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY);
                    purchaseOrderItemService.updateById(orderItem);
                }

                //从耗材表中获取最新的商品注册证号
                if (hospitalConfig != null && GlobalConsts.YES_STR.equals(hospitalConfig.getFlagDeliveryMatrUpdate())) {

                    if (Objects.nonNull(hospitalMaterial)) {

                        if (StringUtils.isNoneBlank(hospitalMaterial.getCertificateNo())) {
                            deliveryItems.forEach(deliveryItem -> deliveryItem.setCertificateNo(hospitalMaterial.getCertificateNo()));
                        }
                        if (StringUtils.isNoneBlank(hospitalMaterial.getMaterialName())) {
                            deliveryItems.forEach(deliveryItem -> deliveryItem.setMaterialName(hospitalMaterial.getMaterialName())
                            );
                        }
                        if (StringUtils.isNoneBlank(hospitalMaterial.getMaterialSpec())) {
                            deliveryItems.forEach(deliveryItem -> deliveryItem.setMaterialSpec(hospitalMaterial.getMaterialSpec())
                            );
                        }
                        if (StringUtils.isNoneBlank(hospitalMaterial.getManufacturerName())) {
                            deliveryItems.forEach(deliveryItem -> deliveryItem.setManufacturerName(hospitalMaterial.getManufacturerName())
                            );
                        }
                    }
                }

                //  将当前发货明细加入采购单的,修改他们的已发数量
                this.deliveryItemService.update(Wrappers.<DeliveryItem>update()
                        .set("delivered_qty", orderItem.getDeliveredQty())
                        .set("certificate_no", deliveryItems.get(0).getCertificateNo())
                        .set("material_name",deliveryItems.get(0).getMaterialName())
                        .set("material_spec",deliveryItems.get(0).getMaterialSpec())
                        .set("manufacturer_name",deliveryItems.get(0).getManufacturerName())
                        .eq("po_item_id", item.getPoItemId()));
            }


            // 生成标签
            deliveryItemSnService.saveDeliveryItemSns(id, null);
            deliveryItemService.updateDeliveredQty(delivery.getId());


            long count = purchaseOrderItemService.count(Wrappers.<PurchaseOrderItem>query()
                    .eq("po_id", delivery.getPoId()).gt("qty-delivered_qty", 0));
            String msgReturn = "发货成功，该订单已全部发货";
            //添加到订单业务进程表
            PurchaseProcess purchaseProcess = new PurchaseProcess();
            purchaseProcess.setHospitalId(delivery.getHospitalId())
                    .setHospitalName(delivery.getHospitalName())
                    .setSupplierId(UserUtils.getSupplierId())
                    .setSupplierName(delivery.getSupplierName())
                    .setPoId(delivery.getPoId())
                    .setGmtCreate(new Date());

            //更新采购订单状态
            PurchaseOrder purchaseOrder = new PurchaseOrder();
            Double deliveryAmount = deliveryItemList.stream().mapToDouble(DeliveryItem::getSubtotalAmount).sum();
            if (count == 0 || delivery.getMaterialTypeCode().equals(MatrTypeCodeConsts.PACKAGE)) { //已全部发货 ---跟台包只需要发一次货
                purchaseOrder.setDeliveryStatus(DictConsts.STATUS_DELIVERYED).setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED).setId(delivery.getPoId());
                //查询该采购订单是否还有未发货的发货单，如果有关闭
                List<Delivery> deliveryList = this.baseMapper.selectList(Wrappers.<Delivery>query().eq("po_id", delivery.getPoId())
                        .eq("po_code", delivery.getPoCode())
                        .eq("hospital_id", delivery.getHospitalId())
                        .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                        .eq("is_closed", DictConsts.STATUS_NO)
                        .ne("id", id)
                );
                if (deliveryList.size() > 0) {
                    //关闭待发货单 关闭原因已全部发货
                    deliveryList.forEach(o -> o.setIsClosed(1).setClosedReason("[点击发货]订单已全部发货，关闭剩下待发货单!"));
                    this.updateBatchById(deliveryList);
                }
                purchaseProcess.setContent(delivery.getPoCode() + "订单全部发货").setType(DictConsts.TYPE_SUPPLIER_ALL_DELIVERYED);
                log.info("[发货-全部发货] \t 发货单号:{} 采购单号:{}", delivery.getCode(), delivery.getPoCode());
            } else {
                purchaseOrder.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY).setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED).setId(delivery.getPoId());
                purchaseProcess.setContent(delivery.getPoCode() + "订单部分发货").setType(DictConsts.TYPE_SUPPLIER_ANY_DELIVERYED);

                //根据采购单id查询采购订单信息
                PurchaseOrder po = purchaseOrderMapper.selectById(delivery.getPoId());
                //生成未发货的商品发货单
                Delivery d = new Delivery();
                d.setPoCode(delivery.getPoCode())
                        .setSupplierId(delivery.getSupplierId()).setSupplierName(delivery.getSupplierName())
                        .setHospitalId(delivery.getHospitalId()).setHospitalName(delivery.getHospitalName())
                        .setPoId(delivery.getPoId()).setOrderAmount(delivery.getOrderAmount()).setGmtAudited(delivery.getGmtAudited())
                        .setDeliveryAmount(deliveryAmount).setWarehouseId(delivery.getWarehouseId())
                        .setWarehouseName(delivery.getWarehouseName()).setIsConsignment(delivery.getIsConsignment())
                        .setIsOneThingOneYard(delivery.getIsOneThingOneYard()).setGmtRequireLatestDelivery(delivery.getGmtRequireLatestDelivery())
                        .setPurchaseDeptId(po.getPurchaseDeptId()).setPurchaseDeptName(po.getPurchaseDeptName()).setPackageName(po.getPackageName())
                        .setMaterialTypeCode(po.getMaterialTypeCode()).setRemark(po.getRemark()).setDemandDeptId(po.getDemandDeptId())
                        .setDemandDeptName(po.getDemandDeptName()).setPurchaseDate(po.getPurchaseDate());
                log.info("[发货-部分发货] \t 发货单号:{} 采购单号:{}", delivery.getCode(), delivery.getPoCode());

                //保存待发货单
                saveDeliverys(d, id);

                msgReturn = StrUtil.format("发货成功，剩余订单已产生新的发货单【{}】",d.getCode());
            }
            purchaseOrderMapper.updateById(purchaseOrder);
            //保存订单进程
            purchaseProcessService.save(purchaseProcess);

            // 更新 发货单 为已经发货状态 1
            Delivery tmp = new Delivery();
            tmp.setDeliveryAmount(deliveryAmount)
                    .setStatus(DictConsts.STATUS_DELIVERY_SENT)
                    .setGmtDelivery(DateUtils.now())
                    .setCreator(UserUtils.currentUser().getNickname())

                    .setId(id);

            boolean b = this.updateById(tmp);
            if (b) {
                tmp.setMsgReturn(msgReturn);
                return tmp;
            }
            return null;
        }
    }

    /**
     * 第二个参数是传点击发货时的发货单id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveDeliverys(Delivery entity, String deliveryId) {
        if (StringUtils.isNotBlank(entity.getPoId()) && deliveryId != null) {
            //查询采购单对应的所有待发货单
            List<Delivery> deliverieList = this.baseMapper.selectList(Wrappers.<Delivery>query().eq("po_id", entity.getPoId())
                    .eq("po_code", entity.getPoCode())
                    .eq("hospital_id", entity.getHospitalId())
                    .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                    .eq("is_closed", DictConsts.STATUS_NO)
                    .ne("id", deliveryId)
            );
            if (deliverieList.size() > 1) {
                throw new BusinessException("该采购单对应的待发货单存在多条，一条采购单只允许存在一条待发货单，请处理");
            }
            if (deliverieList.size() == 1) {
                //如果采购订单对应待发货单已经存在，修改应发实发数量 发货数量
                List<DeliveryItem> deliveryItems = deliveryItemService.list(Wrappers.<DeliveryItem>query().eq("delivery_id", deliverieList.get(0).getId()));
                if (deliveryItems.size() > 0) {
                    for (DeliveryItem item : deliveryItems) {
                        // 查询出采购订单明细
                        PurchaseOrderItem purchaseOrderItem = purchaseOrderItemService.getOne(Wrappers.<PurchaseOrderItem>query()
                                .eq("id", item.getPoItemId()));
                        DeliveryItem di = new DeliveryItem();
                        deliveryItemService.update(di, Wrappers.<DeliveryItem>update()
                                .set("should_delivery_qty", purchaseOrderItem.getQty() - purchaseOrderItem.getDeliveredQty())
                                .set("delivered_qty", purchaseOrderItem.getDeliveredQty())
                                .eq("id", item.getId())
                        );
                    }
                }
                return true;
            }
        }
        //生成单号
        String code = "";
        PrintTemplateVO printTemplate = printTemplateService.findPrintTemplateVO(new PrintTemplateQueryRequest().setHospitalId(entity.getHospitalId())
                .setBusType(BasePrintBusType.DELIVERYCODE.getValue()));
        //查询采购订单对应最新发货单
        Delivery delivery = this.baseMapper.selectOne(Wrappers.<Delivery>query().eq("hospital_id", entity.getHospitalId()).eq("po_code", entity.getPoCode()).orderByDesc("gmt_create").last("limit 1"));
        int count = 0;
        if (delivery == null || delivery.getCode().lastIndexOf("-") == -1) {
            count = count + 1;
        } else {
            //截取最新生成发货单的次数+1
            count = Integer.parseInt(delivery.getCode().substring(delivery.getCode().lastIndexOf("-") + 1)) + 1;
        }
        code = entity.getPoCode() + "-" + count;

        if (StringUtils.isNotBlank(entity.getPoId())) {
            entity.setIsManual(DictConsts.STATUS_NO);
            PurchaseOrder purchaseOrder = purchaseOrderMapper.selectById(entity.getPoId());
            entity.setRemark(entity.getRemark() == null ? purchaseOrder.getRemark() : entity.getRemark());
        } else {
            entity.setIsManual(DictConsts.STATUS_YES);
        }
        entity.setCode(code)
                .setPoCode(entity.getPoCode())
                .setStatus(DictConsts.STATUS_DELIVERY_UNFILLED)
                .setCreator(UserUtils.currentUser().getId())
                .setGmtCreate(DateUtils.now());
        // 新增发货单主表
        boolean saveCount = this.save(entity);
        if (!saveCount) {
            throw new BusinessException("新增发货单失败");
        }
        // 查询出采购订单明细
        List<PurchaseOrderItem> purchaseOrderItemList = purchaseOrderItemService.list(Wrappers.<PurchaseOrderItem>query()
                .eq("po_id", entity.getPoId()).orderByDesc("gmt_create"));
        List<DeliveryItem> deliveryItemList = new ArrayList<>();

        // 采购明细添加到发货明细
        for (PurchaseOrderItem orderItem : purchaseOrderItemList) {

            //  如果数量=发货数量
            if (orderItem.getDeliveredQty().equals(orderItem.getQty())) {
                continue;
            }
            DeliveryItem deliveryItem = new DeliveryItem();
            DeliveryItem deliveryItem2 = new DeliveryItem();
            BeanUtils.copyProperties(orderItem, deliveryItem);
            deliveryItem.setDeliveryId(entity.getId())
                    .setPoItemId(orderItem.getId())
                    .setShouldDeliveryQty(orderItem.getQty() - orderItem.getDeliveredQty())
                    .setDeliveredQty(orderItem.getQty() - orderItem.getDeliveredQty())
                    .setOrderQty(deliveryItem.getOrderUnit() == deliveryItem.getUnit() ? 1.0 : deliveryItem.getOrderQty())
                    .setRemark(orderItem.getRemark())
                    .setMedicalConsumablesCode(orderItem.getMedicalConsumablesCode())
                    .setId(null);

            // 发货数量
            deliveryItem.setDeliveryNumber((int) Math.floor((orderItem.getQty() - orderItem.getDeliveredQty()) / orderItem.getOrderQty()));

            // 发货单品数量
            deliveryItem.setQty(UtilNum.mul(deliveryItem.getDeliveryNumber(), deliveryItem.getOrderQty()));


            deliveryItem.setSubtotalAmount(UtilNum.mul(deliveryItem.getQty(), deliveryItem.getPrice()));
            deliveryItem.setGmtCreate(orderItem.getGmtCreate());
            //如果订单明细 包装单位不等于单品单位
            if (orderItem.getOrderUnit() != orderItem.getUnit()) {
                //实发数量求余数
                Double a = (orderItem.getQty() - orderItem.getDeliveredQty()) % orderItem.getOrderQty();
                if (a != 0.0) {
                    BeanUtils.copyProperties(deliveryItem, deliveryItem2);
                    deliveryItem2.setOrderUnit(orderItem.getUnit()).setOrderQty(1.0)
                            .setDeliveryNumber(Integer.valueOf(a.intValue()))
                            .setQty(a)
                            .setSubtotalAmount(a * orderItem.getPrice());
                    deliveryItemList.add(deliveryItem2);
                }
            }
            if (deliveryItem.getDeliveryNumber() > 0) {
                deliveryItemList.add(deliveryItem);
            }

        }
        // 批量添加
        boolean saveBatchBoo = deliveryItemService.saveBatch(deliveryItemList);
        if (!saveBatchBoo) {
            throw new BusinessException("批量新增发货单明细失败");
        }
        //更新待发货单主表发货金额
        if (deliveryId != null) {
            double totalAmount = deliveryItemList.stream().mapToDouble(o -> o.getSubtotalAmount()).sum();
            this.update(new Delivery(), Wrappers.<Delivery>update()
                    .set("delivery_amount", totalAmount)
                    .eq("id", entity.getId())
            );
        }

        // 更新采购明细已发货数量
        purchaseOrderItemService.updateDeliveredQty(entity.getPoId());

        deliveryItemService.updateDeliveredQty(entity.getId());
        log.info("[新增待发货单] \t 待发货单号:{} 采购单号:{}", code, entity.getPoCode());
        return true;
    }


    @Override
    public IPage<Delivery> sendPage(DeliveryQueryRequest request) {
        request.setSupplierId(UserUtils.getSupplierId()).setStatus(DictConsts.STATUS_DELIVERY_SENT);
        return this.baseMapper.sendPage(request.getPage(), request);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelDelivery(String id) {
        Delivery delivery = this.getById(id);
//        没有发货抛出异常
        if (!delivery.getStatus().equals(DictConsts.STATUS_DELIVERY_SENT)) {
            throw new BusinessException("发货单已经收货，不可取消");
        }

        List<DeliveryItemSn> deliveryItemSnList = deliveryItemSnService.list(Wrappers.<DeliveryItemSn>query().eq("delivery_id", id));
        if (ObjectUtil.length(deliveryItemSnList) > 0) {
            BusinessException.throwError("该发货单已生成相关标签，请在发货单界面手动执行【清除标签】操作后再进行取消发货操作");
        }

        if (delivery.getIsManual() == DictConsts.STATUS_YES) {
//            //  刪除SN
//            deliveryItemSnMapper.delete(Wrappers.<DeliveryItemSn>query()
//                    .eq("delivery_id", id));
            return this.update(delivery, Wrappers.<Delivery>update()
                    .set("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                    .set("gmt_delivery", null)
                    .eq("id", delivery.getId())
            );
        }

        List<DeliveryItem> deliveryItemList = deliveryItemService.list(Wrappers.<DeliveryItem>query()
                .eq("delivery_id", id));

        // 依据订单明细ID 分组，设置订单明细 已发数量
        Map<String, List<DeliveryItem>> map = deliveryItemList.stream().collect(Collectors.groupingBy(o -> o.getPoItemId()));

        // 记录订单明细的已发数量
        Map<String, Double> itemQtyMap = Maps.newHashMap();

        // 记录发货单明细的应发数量
        Map<String, Double> itemShouldQtyMap = Maps.newHashMap();

        for (Map.Entry<String, List<DeliveryItem>> entry : map.entrySet()) {
            DeliveryItem item = entry.getValue().get(0);

            PurchaseOrderItem poItem = purchaseOrderItemService.getById(entry.getKey());
            if (poItem == null) {
                continue;
            }

            Double deliveredQty = poItem.getDeliveredQty();
            double qtys = entry.getValue().stream().mapToDouble(DeliveryItem::getQty).sum();

            PurchaseOrderItem orderItem = new PurchaseOrderItem();

            double orderDeliveryQty = 0.0;
            if (deliveredQty > qtys) {
                orderDeliveryQty = deliveredQty - qtys;
            }

            orderItem.setDeliveredQty(orderDeliveryQty)
                    .setDeliveredAmount(orderDeliveryQty * item.getPrice())
                    .setId(item.getPoItemId());

            if (orderDeliveryQty == 0.0) {
                // 应发 - 本次实发 = 0  修改为未发货
                orderItem.setDeliveryStatus(DictConsts.STATUS_UNDELIVERY);
            } else {
                // 否则修改为部分发货
                orderItem.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY);
            }

            purchaseOrderItemService.updateById(orderItem);
            //订单发货数量
            itemQtyMap.put(poItem.getId(), orderDeliveryQty);
            //应发数量
            itemShouldQtyMap.put(poItem.getId(), poItem.getQty() - orderDeliveryQty);
        }

        for (DeliveryItem item : deliveryItemList) {
            //   把当前明细加入发货订单的,修改 已发数量,还原之前数量
            this.deliveryItemService.update(Wrappers.<DeliveryItem>update()
                    .set("delivered_qty", itemQtyMap.get(item.getPoItemId()))
                    .set("should_delivery_qty", itemShouldQtyMap.get(item.getPoItemId()))
                    .eq("po_item_id", item.getPoItemId()));
//                    .ne("delivery_id", item.getDeliveryId()));
        }
        //查询该订单除了本待发货单是否还有待发货单
        List<Delivery> deliverieList = this.baseMapper.selectList(Wrappers.<Delivery>query().eq("po_id", delivery.getPoId())
                .eq("po_code", delivery.getPoCode())
                .eq("hospital_id", delivery.getHospitalId())
                .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                .eq("is_closed", DictConsts.STATUS_NO)
                .ne("id", delivery.getId())
        );
        if (deliverieList.size() > 1) {
            throw new BusinessException("该采购单对应的待发货单存在多条，一条采购单只允许存在一条待发货单，请处理");
        }
        if (deliverieList.size() == 1) {
            //修改待发货明细应发数量，发货数量
            List<DeliveryItem> deliveryItems = deliveryItemService.list(Wrappers.<DeliveryItem>query().eq("delivery_id", deliverieList.get(0).getId()));
            if (deliveryItems.size() > 0) {
                for (DeliveryItem item : deliveryItems) {
                    DeliveryItem di = new DeliveryItem();
                    if (itemShouldQtyMap.get(item.getPoItemId()) != null) {
                        deliveryItemService.update(di, Wrappers.<DeliveryItem>update()
                                .set("should_delivery_qty", itemShouldQtyMap.get(item.getPoItemId()))
                                .set("delivered_qty", itemQtyMap.get(item.getPoItemId()))
                                .eq("id", item.getId())
                        );
                    }
                }
            }
        }

        //修改采购订单状态 还原之前状态
        PurchaseOrder purchaseOrder = new PurchaseOrder();
        int count = purchaseOrderItemService.count(Wrappers.<PurchaseOrderItem>query().eq("po_id", delivery.getPoId()).in("delivery_status", DictConsts.STATUS_DELIVERYED, DictConsts.STATUS_PART_DELIVERY));
        if (count == 0) {
            purchaseOrder.setDeliveryStatus(DictConsts.STATUS_UNDELIVERY).setStatus(DictConsts.STATUS_SUPPLIER_UNDELIVERY).setId(delivery.getPoId());
        } else {
            purchaseOrder.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY).setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED).setId(delivery.getPoId());
        }
        purchaseOrderMapper.updateById(purchaseOrder);

        //  修改状态 -> 0   修改人  修改时间
        Delivery tmp = new Delivery();
        tmp.setStatus(DictConsts.STATUS_DELIVERY_UNFILLED)
                .setGmtDelivery(null)
                .setId(id);
        //  刪除SN
//        deliveryItemSnMapper.delete(Wrappers.<DeliveryItemSn>query()
//                .eq("delivery_id", id));
        // 修改主表
        boolean b = this.updateById(tmp);
        log.info("[取消发货] \t 发货单号:{}", delivery.getCode());
        return b;
    }

    /**
     * 发货单作废
     */
    @Override
    public void closeDelivery(CloseDeliveryDTO dto) {
        Delivery delivery = this.getById(dto.getId());

        // 判断当前发货单是否存在
        if (delivery == null) {
            throw new BusinessException("未查询到相关发货单信息");
        }
        // 判断当前发货单状态是否已收货
        if (delivery.getStatus() == DictConsts.STATUS_DELIVERY_RECEIVE) {
            throw new BusinessException("该发货单已发货完成，无法关闭");
        }
        //修改所有待发货单状态为关闭
        this.update(Wrappers.<Delivery>update()
                .set("is_closed", DictConsts.STATUS_YES)
                .set("closed_reason", dto.getReason())
                .eq("id", delivery.getId())
        );
        //查询出该订单对应除了该发货单对应的所有待发货
        List<Delivery> deliveries = this.baseMapper.selectList(Wrappers.<Delivery>query().eq("po_id", delivery.getPoId())
                .eq("supplier_id", UserUtils.getSupplier().getId())
                .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                .ne("id", delivery.getId()));
        if (deliveries != null && deliveries.size() > 0) {
            //修改所有待发货单状态为关闭
            deliveries.forEach(o -> o.setIsClosed(DictConsts.STATUS_YES).setClosedReason("[待发货单号]:" + delivery.getCode() + "作废,相应作废该待发货单"));
            this.updateBatchById(deliveries);
        }

        //修改采购订单关闭状态
        PurchaseOrder purchaseOrder = purchaseOrderMapper.selectById(delivery.getPoId());

        // 判断当前订单是否存在
        if (purchaseOrder == null) {
            throw new BusinessException("未查询到相关订单信息");
        }

        // 判断当前订单状态是否完成
        if (purchaseOrder.getStatus() == DictConsts.STATUS_COMPLETE) {
            throw new BusinessException("该订单已完成，无法关闭");
        }

        // 判断当前订单是否关闭
        if (purchaseOrder.getIsClosed() != DictConsts.STATUS_NO) {
            throw new BusinessException("该订单已关闭，无法再次关闭");
        }

        // 修改是否关闭状态为 关闭，修改订单状态为 完成
        purchaseOrderMapper.update(new PurchaseOrder(), Wrappers.<PurchaseOrder>update()
                .set("is_closed", DictConsts.STATUS_YES)
                .set("status", DictConsts.STATUS_COMPLETE)
                .set("closed_reason", dto.getReason())
                .eq("id", purchaseOrder.getId()));

        log.info("[发货单作废] \t 发货单号:{} 采购单号:{} 作废原因:{}", delivery.getCode(), delivery.getPoCode(), dto.getReason());
    }

    @Override
    public IPage allDeliveryPage(DeliveryQueryRequest request) {
        IPage page = this.baseMapper.allDeliveryPage(request.getPage(), request);
        return page;
    }

    @Override
    public DeliveryVO getDeliveryByCode(String code) {

        String hospitalId = UserUtils.getHospitalId();
        //获取主表信息
//        Delivery delivery = this.getOne(Wrappers.<Delivery>query().eq("code", code).eq("status", DictConsts.STATUS_DELIVERY_SENT).eq("hospital_id", hospitalId));
        Delivery delivery = this.getOne(Wrappers.<Delivery>query().eq("code", code).eq("hospital_id", hospitalId));
        if (delivery == null) {
            throw new BusinessException("未查询到该发货单");
        }
        if (!DictConsts.STATUS_DELIVERY_SENT.equals(delivery.getStatus())) {
            throw new BusinessException("发货单不是已发货状态");
        }

        DeliveryVO deliveryVO = new DeliveryVO();
        BeanUtils.copyProperties(delivery, deliveryVO);

        // 供应商手机号
        Supplier supplier = supplierService.getById(delivery.getSupplierId());
        deliveryVO.setMobile(supplier.getMobile()).setContactor(supplier.getContactor());

        // 获取采购订单主表的来源标识
        PurchaseOrder purchaseOrder = purchaseOrderMapper.selectById(delivery.getPoId());
        if (purchaseOrder == null) {
            throw new BusinessException("数据异常");
        }
        deliveryVO.setSourceKey(purchaseOrder.getSourceKey());
        deliveryVO.setIsClosed(purchaseOrder.getIsClosed());
        deliveryVO.setFromType(purchaseOrder.getFromType());

        if (StringUtils.isNotBlank(purchaseOrder.getWarehouseId()) && DictConsts.PO_FROM_TYPE_SCP.equals(purchaseOrder.getFromType())) {
            HospitalWarehouse hospitalWarehouse =  hospitalWarehouseService.getById(purchaseOrder.getWarehouseId());
            BusinessException.throwErrorIf(hospitalWarehouse == null, "收货仓库不存在");
            BusinessException.throwErrorIf(StringUtils.isBlank(hospitalWarehouse.getCode()), "收货仓库代码不存在");
            deliveryVO.setWarehouseCode(hospitalWarehouse.getCode());
        }



        //获取明细列表
        List<DeliveryItemVO> items = deliveryItemService.list(new DeliveryItemQueryRequest().setDeliveryId(delivery.getId()));
        if (items.size() == 0) {
            return deliveryVO;
        }

        if (GlobalConsts.YES_STR.equalsIgnoreCase(deliveryVO.getIsOneThingOneYard())) {
            items.forEach(o -> {
                List<String> snList = deliveryItemSnService.listObjs(
                        Wrappers.<DeliveryItemSn>query()
                                .select("sn")
                                .eq("delivery_item_id", o.getId())
                        , i -> i.toString());
                o.setSns(snList);
            });
            items.forEach(o -> {
                List<DeliveryItemSn> snUdiList = deliveryItemSnService.list(
                        Wrappers.<DeliveryItemSn>query()
                                .select("sn,udi")
                                .eq("delivery_item_id", o.getId()));
                o.setListSnUdi(snUdiList);
            });
        }


        // 获取采购订单子表的来源标识
        items.stream().forEach(o -> {
            o.setSourceKey(purchaseOrderItemService.getById(o.getPoItemId()).getSourceKey());
            // 商品代码为空的填充
            if (StringUtils.isBlank(o.getMaterialCode())) {
                BusinessException.throwErrorIf(StringUtils.isBlank(o.getMaterialSpecId()), "发货明细{}商品规格为空，请联系管理员检查",o.getMaterialName());
                HospitalMaterial hospitalMaterialFind = hospitalMaterialService.getOne(Wrappers.<HospitalMaterial>query().eq("hospital_id",delivery.getSupplierId()).eq("material_id",o.getMaterialSpecId()), false);
                BusinessException.throwErrorIf(ObjectUtil.isNull(hospitalMaterialFind), "发货明细{}商品规格{}没有找到，请联系管理员检查",o.getMaterialName(),o.getMaterialSpecId());
                o.setMaterialCode(hospitalMaterialFind.getMaterialCode());
            }
        });

        deliveryVO.setItems(items);

        return deliveryVO;
    }

    @Override
    public void notifyByCode(String code) {
        DeliveryVO deliveryVO = this.getDeliveryByCode(code);

        if (deliveryVO == null) {
            log.info("[入库通知接口] \t 发货单号{}不存在", code);
            throw new BusinessException("发货单号不存在");
        }

        if (!DictConsts.STATUS_DELIVERY_SENT.equals(deliveryVO.getStatus())) {
            throw new BusinessException("发货单当前不是已发货状态，不支持收货操作");
        }

        PurchaseOrder purchaseOrder = purchaseOrderMapper.selectById(deliveryVO.getPoId());
        if (purchaseOrder.getStatus() != DictConsts.STATUS_SUPPLIER_DELIVERYED) {
            return;
        }

        //  修改订单详情表已收货数量
        List<DeliveryItem> list = deliveryItemService.list(Wrappers.<DeliveryItem>query().eq("delivery_id", deliveryVO.getId()));
        for (DeliveryItem item : list) {
            purchaseOrderItemService.update(Wrappers.<PurchaseOrderItem>update()
                    .eq("id", item.getPoItemId())
                    .setSql("stored_qty = stored_qty + " + item.getQty())
            );
        }

        //  根据订单详情表 所有 已收货数量 = 订单数量 判断订单状态 完成9
        List<PurchaseOrderItem> orderItems = purchaseOrderItemService.list(Wrappers.<PurchaseOrderItem>query().eq("po_id", deliveryVO.getPoId()));
        boolean receiveDone = true;
        for (PurchaseOrderItem orderItem : orderItems) {
            if (!orderItem.getStoredQty().equals(orderItem.getQty())) {
                receiveDone = false;
                break;
            }
        }
        if (receiveDone) {
            purchaseOrderMapper.update(purchaseOrder, Wrappers.<PurchaseOrder>update()
                    .eq("id", deliveryVO.getPoId())
                    .set("status", DictConsts.STATUS_COMPLETE)
            );
            //添加到订单业务进程表 确认不可供货
            PurchaseProcess purchaseProcess = new PurchaseProcess();
            purchaseProcess.setHospitalId(purchaseOrder.getHospitalId())
                    .setHospitalName(purchaseOrder.getHospitalName())
                    .setSupplierId(purchaseOrder.getSupplierId())
                    .setSupplierName(purchaseOrder.getSupplierName())
                    .setPoId(purchaseOrder.getId())
                    .setGmtCreate(new Date());

            purchaseProcess.setContent(purchaseOrder.getCode() + "订单已完成").setType(DictConsts.TYPE_COMPLETE);


        }

        //根据code获取数据
        this.update(Wrappers.<Delivery>update()
                .eq("code", code).eq("hospital_id", UserUtils.getHospitalId())
                .set("status", DictConsts.STATUS_DELIVERY_RECEIVE)
        );
        pushedRecordService.pushVxPoInAsync(purchaseOrder);
        log.info("[入库通知] \t 发货单号:{} 入库单号:{}", deliveryVO.getCode(), deliveryVO.getPoCode());
    }

    @Override
    public DeliveryVO getDeliveryForSupplier(String code, String hospitalId) {
        //查询发货单数据
        String supplierId = UserUtils.getSupplierId();
//        String supplierId = "1244510289349677057";
        Delivery delivery = this.getOne(Wrappers.<Delivery>query()
                .eq("code", code)
                .eq("hospital_id", hospitalId)
                .eq("supplier_id", supplierId)
                .last("limit 1")
        );
        if (delivery == null) {
            throw new BusinessException("未查询到数据");
        }
        DeliveryVO deliveryVO = new DeliveryVO();
        BeanUtils.copyProperties(delivery, deliveryVO);

        //查询发货单明细
        List<DeliveryItem> itemList = deliveryItemService.list(Wrappers.<DeliveryItem>query().eq("delivery_id", delivery.getId()));
        List<DeliveryItemVO> voList = Lists.newArrayListWithCapacity(itemList.size());
        com.scs.application.core.utils.BeanUtils.copyProperties(itemList, voList, DeliveryItemVO.class);

        deliveryVO.setItems(voList);
        return deliveryVO;

    }


    /**
     * 获取发货单二维码字符串列表，2个明细行为一个二维码字符串
     */
    @Override
    public DeliveryZip getDeliveryItemQrCodeString(String deliveryId) {

        DeliveryZip deliveryzip = new DeliveryZip();

        //主发货单压缩
        Delivery delivery = this.getById(deliveryId);
        if (delivery == null) {
            return (DeliveryZip) Collections.emptyList();
        }

        DeliveryVO deliveryVO = new DeliveryVO();
        BeanUtils.copyProperties(delivery, deliveryVO);
        PurchaseOrder po = purchaseOrderMapper.selectOne(Wrappers.<PurchaseOrder>query().eq("id", deliveryVO.getPoId()));

        //如果为手工单
        if (delivery.getIsManual() == DictConsts.STATUS_YES) {
            deliveryVO = new DeliveryVO().setCode(deliveryVO.getCode())
                    .setSupplierId(deliveryVO.getSupplierId())
                    .setIsOneThingOneYard(deliveryVO.getIsOneThingOneYard());
        } else {//否则为非手工单
            deliveryVO = new DeliveryVO().setCode(deliveryVO.getCode()).setSourceKey(po.getSourceKey());
        }

        String dzip = ZipUtils.zip(JSON.toJSONString(deliveryVO));
        deliveryzip.setDelivery(dzip);

        //发货单详情压缩
        List<DeliveryItemVO> itemList = deliveryItemService.list(new DeliveryItemQueryRequest().setDeliveryId(deliveryId));

        // 一物一码，查询SN
        if (GlobalConsts.YES_STR.equals(delivery.getIsOneThingOneYard())) {
            itemList.forEach(o -> {
                List<String> snList = deliveryItemSnService.listObjs(
                        Wrappers.<DeliveryItemSn>query()
                                .select("sn")
                                .eq("delivery_item_id", o.getId())
                        , i -> i.toString());
                if (snList.isEmpty()) {
                    throw new BusinessException("sn码为空，请先打印sn码!");
                }
                o.setSns(snList);
            });
        }

        // 二维码中只需要部分数据
        List<DeliveryItemVO> resultItems = Lists.newArrayListWithCapacity(itemList.size());
        for (DeliveryItemVO item : itemList) {
            DeliveryItemVO tmp = new DeliveryItemVO();

            //如果为手工单
            if (delivery.getIsManual() == DictConsts.STATUS_YES) {
                tmp.setQty(item.getQty()).setLot(item.getLot()).setProductionDate(item.getProductionDate())
                        .setExpiredDate(item.getExpiredDate()).setSourceKey(item.getId()).setSns(item.getSns()).setMaterialCode(item.getMaterialCode()).setPrice(item.getPrice());
            } else {//否则为非手工单
                tmp.setQty(item.getQty()).setLot(item.getLot()).setProductionDate(item.getProductionDate())
                        .setExpiredDate(item.getExpiredDate()).setSourceKey(item.getSourceKey()).setSns(item.getSns());
            }

            resultItems.add(tmp);
        }

        int resultSize = resultItems.size();

        List<String> result = Lists.newArrayListWithCapacity(resultSize);

        //一个二维码包含2条数据
        int pageSize = 2;
        int qrCodeCount = 1;
        if (resultSize > pageSize) {
            qrCodeCount = (resultSize % pageSize == 0 ? resultSize / pageSize : resultSize / pageSize + 1);
        }

        // 将数据转成JSON字符串，压缩后返回
        for (int i = 0; i < qrCodeCount; i++) {
            int start = i * pageSize;
            int end = (i + 1) * pageSize > resultSize ? resultSize : (i + 1) * pageSize;

            List listZip = resultItems.subList(start, end);

            String z = ZipUtils.zip(JSON.toJSONString(listZip));
            result.add(z);
        }
        deliveryzip.setDeliveryItem(result);
        return deliveryzip;
    }

    /**
     * ERP 发货
     * @param request
     * @return
     */
    @Override
    public CommonResult sendDeliveryERP(DeliveryRequest request) {

        /** 1 查询订单数据 */
        // 1.1 查询采购订单
        PurchaseOrder po = purchaseOrderMapper.selectOne(Wrappers.<PurchaseOrder>query().eq("code", request.getPoCode()).eq("hospital_id", request.getHospitalId()));
        if (po == null){
            return CommonResult.error("未知的发货单，执行发货失败.");
        }
        // 1.2 查询采购订单明细
        List<PurchaseOrderItem> poItemList = purchaseOrderItemService.list(Wrappers.<PurchaseOrderItem>query().eq("po_id", po.getId()));
        Map<String, PurchaseOrderItem> poItemIdMap = poItemList.stream().collect(Collectors.toMap(o -> o.getMaterialErpCode(), o -> o, (o1, o2) -> o1));

        /** 2 校验各项通过条件 */

        if(request.getPoStatus() != 0) {
            // 2.1 校验合法的发货状态：  注意不能让第三方接口过来的订单状态随意取值，这里需要校验一下状态是否正确
            String status = DictConsts.TYPE_SUPPLIER_ANY_DELIVERYED == request.getPoStatus() || DictConsts.TYPE_SUPPLIER_ALL_DELIVERYED == request.getPoStatus() ? request.getPoStatus() + "" : null;
            if (status == null) {
                return CommonResult.error("未知的发货状态.");
            }
        }
        // 2.2 校验合法的发货明细
        // （参数）发货明细Items
        List<DeliveryRequest.DeliveryItem> dyItemList = request.getItems();
        // (参数) 发货明细Third id
        Set<String> dyPoItemIdList = dyItemList.stream().map(DeliveryRequest.DeliveryItem::getThirdMaterialId).collect(Collectors.toSet());
        // 采购订单明细 Item id
        Set<String> poItemIdSet = poItemIdMap.keySet();

        // 2.3 无明细不可发货
        if (dyItemList.isEmpty() || dyItemList.size() == 0) {
            return CommonResult.error("没有明细,无法发货.");
        }

        // 2.4 校验不在采购订单明细中的商品
        dyPoItemIdList.removeAll(poItemIdSet);
        if(!dyPoItemIdList.isEmpty()){
            return CommonResult.error("未知的发货明细.", dyPoItemIdList);
        }

        // 2.5 查询订单是否关闭
        if(po.getIsClosed() == DictConsts.CLOSED_YES){
            return CommonResult.error("该订单被关闭,无法进行发货.");
        }

        /** 3 生成发货单 */   // TODO leisq 这里可以考虑再简化一下参数输入，避免创建订单的时候set参数的代码写得太长
        // 生成送货单号
        final String code = createDelivery.generateCode(po.getHospitalId(), po.getCode());

        // 编辑主表
        DeliveryDTO dDto = new DeliveryDTO();
        BeanUtils.copyProperties(po,dDto);
        dDto.setId("")
                .setPoId(po.getId())
                .setPoCode(po.getCode())
                .setIsManual(DictConsts.STATUS_NO)
                .setCode(code)
                .setStatus(DictConsts.STATUS_DELIVERY_UNFILLED)
                .setOrderAmount(po.getTotalAmount())
                .setThirdDeliveryId(request.getThirdOrderId())
                .setGmtAudited(request.getGmtAcutalDelivery())
                .setGmtDelivery(request.getGmtAcutalDelivery());

        // 编辑明细
        List<DeliveryItemDTO> itemDtos = Lists.newArrayListWithCapacity(dyItemList.size());
        for (DeliveryRequest.DeliveryItem item : dyItemList){
            String materialId = item.getThirdMaterialId();
            // 把采购明细的属性赋值给发货明细
            PurchaseOrderItem orderItem = poItemIdMap.get(materialId);
            DeliveryItemDTO deliveryItem = new DeliveryItemDTO();
            BeanUtils.copyProperties(orderItem, deliveryItem);
            // 模拟页面上填写发货单，（这样靠谱点）
            deliveryItem.setId("")
                    .setQty(item.getQty())
                    .setSubtotalAmount(NumberUtils.multiplyDouble(item.getQty(), deliveryItem.getPrice()))
                    .setLot(item.getLot())
                    .setSaleLot(item.getSaleLot())
                    .setMaterialCode(item.getMaterialCode())
                    .setThirdDistId(item.getThirdDistId())
                    .setProductionDate(item.getProductionDate())
                    .setExpiredDate(item.getExpiredDate())
                    .setSterilizationDate(item.getSterilizationDate())
                    .setCertificateNo(item.getCertificateNo());

            deliveryItem.setPoItemId(orderItem.getId());
//            deliveryItem.setShouldDeliveryQty(orderItem.getQty() - orderItem.getDeliveredQty());
//            deliveryItem.setDeliveredQty(orderItem.getDeliveredQty());

            if(StringUtils.isBlank(deliveryItem.getCertificateNo())){
                //从耗材表中获取最新的商品注册证号
                HospitalMaterialRequest hospitalMaterialRequest = new HospitalMaterialRequest();
                hospitalMaterialRequest.setCode(orderItem.getMaterialCode())
                        .setHospitalId(po.getHospitalId())
                        .setSupplierId(po.getSupplierId());
                HospitalMaterial hospitalMaterial = hospitalMaterialService.getOneHospitalMaterial(hospitalMaterialRequest);
                if (hospitalMaterial != null && StringUtils.isNoneBlank(hospitalMaterial.getCertificateNo())) {
                    deliveryItem.setCertificateNo(hospitalMaterial.getCertificateNo());
                }
            }

            // 添加明细入list集合
            itemDtos.add(deliveryItem);
        }

        // 发货实体 Set完毕
        dDto.setItems(itemDtos);
        //发货发货单
        Delivery delivery = createDelivery.create(dDto);

        /** 4 执行发货*/
        this.deliveryErp.deliver(delivery, po);
        return CommonResult.ok("发货成功");
    }

    /**
     * 第三方接口，供方保存发货单
     */
    @Override
    public void batchSave(List<DeliveryDTO> deliveryDTOS) {
        List<Delivery> list = Lists.newArrayListWithCapacity(deliveryDTOS.size());
        for (DeliveryDTO dto : deliveryDTOS) {
            Delivery d = new Delivery();
            //查询采购单是否存在
            PurchaseOrder order = purchaseOrderMapper.selectOne(Wrappers.<PurchaseOrder>query().eq("code", dto.getPoCode()).eq("hospital_id", UserUtils.getHospitalId()));
            if (order == null) {
                throw new BusinessException(MessageFormat.format("采购单不存在，不能添加", dto.getPoCode()));
            }
            //计算发货金额
            List<DeliveryItem> tmpList = Lists.newArrayListWithExpectedSize(dto.getItems().size());
            com.scs.application.core.utils.BeanUtils.copyProperties(dto.getItems(), tmpList, DeliveryItem.class);
            //计算发货总金额=发货明细金额加和
            Double deliveryAmount = tmpList.stream().collect(Collectors.summingDouble(DeliveryItem::getSubtotalAmount));
           /* //如果发货总金额=订单总金额修改发货状态为全部发货，否则部分发货
            if(deliveryAmount == order.getTotalAmount()){
                d.setStatus(DictConsts.STATUS_DELIVERY_RECEIVE);
            }else{
                d.setStatus(DictConsts.STATUS_DELIVERY_SENT);
            }*/

            d.setHospitalId(order.getHospitalId())
                    .setHospitalName(order.getHospitalName())
                    .setCode(dto.getCode())
                    .setPoId(order.getId())
                    .setPoCode(order.getCode())
                    .setSupplierId(order.getSupplierId())
                    .setSupplierName(order.getSupplierName())
                    .setWarehouseId(order.getWarehouseId())
                    .setWarehouseName(order.getWarehouseName())
                    .setIsManual(DictConsts.STATUS_NO)
                    .setIsConsignment(order.getIsConsignment())
                    .setIsOneThingOneYard(order.getIsOneThingOneYard())
                    .setGmtRequireLatestDelivery(order.getGmtRequireLatestDelivery())
                    .setGmtRequireLatestDelivery(order.getGmtRequireLatestDelivery())
                    .setGmtActualDelivery(dto.getGmtActualDelivery())
                    .setOrderAmount(order.getTotalAmount())
                    .setDeliveryAmount(deliveryAmount)
                    .setGmtDelivery(dto.getGmtDelivery())
                    .setStatus(DictConsts.STATUS_DELIVERY_SENT);
            list.add(d);

        }

        // 保存主表
        boolean saveCount = saveBatch(list);
        if (!saveCount) {
            throw new BusinessException("新增发货单失败");
        }

        Map<String, String> map = list.stream().collect(Collectors.toMap(o -> o.getCode(), o -> o.getId(), (o1, o2) -> o1));

        List<DeliveryItem> itemList = new ArrayList<>();
        List<PurchaseOrderItem> pItemList = new ArrayList<>();

        for (DeliveryDTO dto : deliveryDTOS) {

            List<DeliveryItem> tmpList = Lists.newArrayListWithExpectedSize(dto.getItems().size());
            com.scs.application.core.utils.BeanUtils.copyProperties(dto.getItems(), tmpList, DeliveryItem.class);
            tmpList.stream().forEach(o -> o.setDeliveryId(map.get(dto.getCode())));

            //根据code查询采购单信息
            // PurchaseOrder p =purchaseOrderMapper.getOne(Wrappers.<PurchaseOrder>query().eq("code", dto.getPoCode()));

            // 查询出采购订单明细
            for (DeliveryItem item : tmpList) {

                //获取发货单明细对应的采购明细
                //PurchaseOrderItem purchaseOrderItem = purchaseOrderItemService.getOne(Wrappers.<PurchaseOrderItem>query().eq("po_id", p.getId()));
                PurchaseOrderItem purchaseOrderItem = purchaseOrderItemService.getOne(Wrappers.<PurchaseOrderItem>query().eq("id", item.getPoItemId()));
                item.setMaterialSpecId(purchaseOrderItem.getMaterialSpecId());
                //如果注册证号为空则从hospitalMaterial表查
                if (StringUtils.isBlank(item.getCertificateNo())) {
                    List<HospitalMaterialVO> hospitalMaterialByCode = hospitalMaterialService.listVO(new HospitalMaterialRequest().setCode(purchaseOrderItem.getMaterialCode()));
                    if (!hospitalMaterialByCode.isEmpty()) {
                        item.setCertificateNo(hospitalMaterialByCode.get(0).getCertificateNo());
                    }
                }

                PurchaseOrderItem pitem = new PurchaseOrderItem();
                //  如果已发数量=采购数量
                if (purchaseOrderItem.getDeliveredQty().equals(purchaseOrderItem.getQty())) {
                    continue;
                }

                //更新采购单信息
                //发货数量=已发数量+本次实发 发货金额=已发数量*发货金额
                Double dQty = purchaseOrderItem.getDeliveredQty() + item.getQty();
                pitem.setDeliveredQty(dQty).setDeliveredAmount(dQty * purchaseOrderItem.getPrice()).setId(purchaseOrderItem.getId());

                //  如果订单数量=发货数量
                if (dQty.equals(pitem.getQty())) {
                    //已发货
                    pitem.setDeliveryStatus(DictConsts.STATUS_DELIVERYED);
                } else if (dQty > 0) {
                    //已部分发货
                    pitem.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY);
                } else {
                    //未发货
                    pitem.setDeliveryStatus(DictConsts.STATUS_UNDELIVERY);
                }
                pItemList.add(pitem);

            }

            itemList.addAll(tmpList);

        }

        //修改采购单明细
        purchaseOrderItemService.updateBatchById(pItemList);
        // 保存发货单明细表
        deliveryItemService.saveBatch(itemList);

        //更新采购订单状态

        for (DeliveryDTO dto : deliveryDTOS) {
            PurchaseOrder purchaseOrder = new PurchaseOrder();

            Delivery delivery = this.getOne(Wrappers.<Delivery>query().eq("po_code", dto.getPoCode()).eq("hospital_id", UserUtils.getHospitalId()));
            int count = purchaseOrderItemService.count(Wrappers.<PurchaseOrderItem>query().eq("po_id", delivery.getPoId()).in("delivery_status", DictConsts.STATUS_UNDELIVERY, DictConsts.STATUS_PART_DELIVERY));
            if (count == 0) {
                purchaseOrder.setDeliveryStatus(DictConsts.STATUS_DELIVERYED).setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED).setId(delivery.getPoId());

            } else {
                purchaseOrder.setDeliveryStatus(DictConsts.STATUS_PART_DELIVERY).setStatus(DictConsts.STATUS_SUPPLIER_DELIVERYED).setId(delivery.getPoId());
            }
            purchaseOrderMapper.updateById(purchaseOrder);

        }


    }

    /**
     * 模板导出查询
     */
    @Override
    public IPage<ExcelDeliveryVO> pageExcel(DeliveryQueryRequest request) {
        request.setStatus(DictConsts.STATUS_DELIVERY_UNFILLED).setIsManual(DictConsts.STATUS_NO);
        return this.baseMapper.pageExcel(request.getPage(), request);
    }

    /**
     * 导出查询
     */
    @Override
    public List<ExcelDeliveryVO> listExcel(DeliveryQueryRequest request) {
        request.setStatus(DictConsts.STATUS_DELIVERY_UNFILLED).setIsManual(DictConsts.STATUS_NO);
        return this.baseMapper.listExcel(request);
    }

    /**
     * 发货单导入
     */
    @Override
    protected ExcelImportResult doExcelImport(MultipartFile multipartFile, Map<String, Object> params) throws Exception {
        ImportParams importParams = PoiUtils.getDefaultImportParams();
        ExcelImportResult importResult = ExcelImportUtilCustom.importExcelMore(multipartFile.getInputStream(), ExcelDeliveryDTO.class, importParams);
        List<ExcelDeliveryDTO> dtoList = Lists.newArrayList(importResult.getList());

        if (dtoList.size() == 0) {
            return importResult;
        }

        //List<DeliveryItemDTO> itemDTOS = Lists.newArrayListWithCapacity(dtoList.size());
        //List<DeliveryItem> items = Lists.newArrayListWithCapacity(itemDTOS.size());

        Map<String, List<HospitalSupplierVO>> hospitalSupplierMap = null;
        Map<String, List<ExcelDeliveryDTO>> map = null;

        //查询出该供应商下所有医院,存放到map中
        List<HospitalSupplierVO> hospitalSupplierList = hospitalSupplierService.listVO(new HospitalSupplierRequest());
        if (hospitalSupplierList.isEmpty() || hospitalSupplierList == null) {
            throw new BusinessException("该供应商下没有医院");
        }
        hospitalSupplierMap = hospitalSupplierList.parallelStream().collect(Collectors.groupingBy(o -> o.getHospitalName()));

        // 数据分组；EXCEL数据 品名和品规在同一行，所有需要将数据进行分组
        map = dtoList.stream().collect(Collectors.groupingBy(o -> o.getCode()));

        //分组后把每一组存入ldtoList
        List<List<ExcelDeliveryDTO>> ldtoList = Lists.newArrayListWithCapacity(map.size());
        for (String key : map.keySet()) {
            ldtoList.add(map.get(key));
        }

        Map<String, List<HospitalSupplierVO>> finalHospitalSupplierMap = hospitalSupplierMap;
        String supplierId = UserUtils.getSupplierId();
        String supplierName = UserUtils.getSupplier().getName();
        DefaultSaveHandler<List<ExcelDeliveryDTO>> saveHandler = new DefaultSaveHandler<List<ExcelDeliveryDTO>>() {
            @Override
            public void doSave(int currentIndex, List<ExcelDeliveryDTO> listentity) throws BusinessException {
                // 先保存发货单数据，由于已经经过分组，所以list中数据关于发货单的数据一致，直接取第一行
                ExcelDeliveryDTO edDTO = listentity.get(0);
                Delivery d = new Delivery();
                BeanUtils.copyProperties(edDTO, d);
                d.setSupplierId(supplierId);
                d.setSupplierName(supplierName);
                List<HospitalSupplierVO> hospitalSupplierVOS = finalHospitalSupplierMap.get(edDTO.getHospitalName());
                if (hospitalSupplierVOS == null || hospitalSupplierVOS.isEmpty()) {
                    importResult.getList().remove(edDTO);
                    edDTO.setErrorMsg(String.format("医院不存在"));
                    importResult.getFailList().add(edDTO);
                    log.warn("第{}行医院不存在，请先确认数据是否正确；医院名称：{}", edDTO.getRowNum(), edDTO.getHospitalName());
                    return;
                    // continue;
                }
                d.setHospitalId(hospitalSupplierVOS.get(0).getHospitalId());
                d.setHospitalName(hospitalSupplierVOS.get(0).getHospitalName());
                PurchaseOrder purchaseOrder = purchaseOrderMapper.selectOne(Wrappers.<PurchaseOrder>query()
                        .eq("code", edDTO.getPoCode())
                        .eq("supplier_id", supplierId)
                        .last("limit 1"));
                if (purchaseOrder == null) {
                    importResult.getList().remove(edDTO);
                    edDTO.setErrorMsg(String.format("该供应商采购单不存在"));
                    importResult.getFailList().add(edDTO);
                    log.warn("第{}行该供应商采购单号不存在，请先确认数据是否正确；采购单号：{}", edDTO.getRowNum(), edDTO.getPoCode());
                    return;
                    //continue;
                }
                d.setPoId(purchaseOrder.getId())
                        .setPoCode(purchaseOrder.getCode())
                        .setWarehouseId(purchaseOrder.getWarehouseId())
                        .setWarehouseName(purchaseOrder.getWarehouseName())
                        .setIsConsignment(purchaseOrder.getIsConsignment())
                        .setIsOneThingOneYard(purchaseOrder.getIsOneThingOneYard())
                        .setGmtRequireLatestDelivery(purchaseOrder.getGmtRequireLatestDelivery());

                //查询数据库是否有该发货单，如果有就更新，没有就保存
                Delivery delivery = getOne(Wrappers.<Delivery>query()
                        .eq(StringUtils.isNotBlank(d.getCode()), "code", d.getCode())
                        .eq(StringUtils.isNotBlank(d.getHospitalName()), "hospital_name", d.getHospitalName())
                        .eq(StringUtils.isNotBlank(d.getPoCode()), "po_code", d.getPoCode())
                        .eq("status", DictConsts.STATUS_DELIVERY_UNFILLED)
                        .eq("is_manual", DictConsts.STATUS_NO)
                        .last("limit 1")
                );

                if (delivery != null) {
                    d.setId(delivery.getId());
                    updateById(d);
                } else {
                    //发货单状态(制单)
                    d.setStatus(DictConsts.STATUS_DELIVERY_UNFILLED)
                            .setIsManual(DictConsts.STATUS_NO)
                            .setDeliveryAmount(1.0);  //default
                    save(d);
                }

                // 保存发货单明细
                List<DeliveryItemDTO> itemDTOS = Lists.newArrayListWithCapacity(listentity.size());
                ArrayList<DeliveryItem> items = Lists.newArrayListWithCapacity(itemDTOS.size());
                listentity.stream().forEach(o -> o.setDeliveryId(d.getId()));
                for (ExcelDeliveryDTO dto : listentity) {
                    //查询数据库该发货单明细是否存在，存在就更新不存在就保存
                    DeliveryItem deliveryItem = deliveryItemService.getOne(Wrappers.<DeliveryItem>query()
                            .eq(StringUtils.isNotBlank(dto.getDeliveryId()), "delivery_id", dto.getDeliveryId())
                            .eq(StringUtils.isNotBlank(dto.getMaterialSpec()), "material_spec", dto.getMaterialSpec())
                            .eq(StringUtils.isNotBlank(dto.getMaterialName()), "material_name", dto.getMaterialName())
                            .eq(StringUtils.isNotBlank(dto.getLot()), "lot", dto.getLot())
                            .last("limit 1")
                    );
                    DeliveryItemDTO item = new DeliveryItemDTO();
                    if (deliveryItem != null) {
                        item.setId(deliveryItem.getId());
                    }
                    //查询条件下采购单明细信息
                    PurchaseOrderItem pitem = purchaseOrderItemService.getOne(Wrappers.<PurchaseOrderItem>query()
                            .eq(StringUtils.isNotBlank(dto.getPoId()), "po_id", dto.getPoCode())
                            .eq(StringUtils.isNotBlank(dto.getMaterialName()), "material_name", dto.getMaterialName())
                            .eq(StringUtils.isNotBlank(dto.getMaterialSpec()), "material_spec", dto.getMaterialSpec())
                            .last("limit 1")
                    );
                    if (pitem != null) {
                        item.setPoItemId(pitem.getId())
                                .setMaterialSpecId(pitem.getMaterialSpecId())
                                .setMaterialSpec(dto.getMaterialSpec())
                                .setMaterialCode(pitem.getMaterialCode())
                                .setMaterialErpCode(pitem.getMaterialErpCode())
                                .setMaterialTradeName(pitem.getMaterialTradeName())
                                .setManufacturerName(pitem.getManufacturerName())
                                .setForm(pitem.getForm())
                                .setPrice(pitem.getPrice())
                                .setShouldDeliveryQty(pitem.getQty())
                                .setOrderQty(pitem.getOrderQty())
                                .setOrderUnit(pitem.getOrderUnit())
                                .setDeliveredQty(pitem.getDeliveredQty())
                                .setUnit(pitem.getUnit())
                                .setCertificateNo(pitem.getCertificateNo())
                                .setDeliveryId(dto.getDeliveryId())
                                .setMaterialName(dto.getMaterialName())
                                .setQty(dto.getQty())
                                .setSubtotalAmount(item.getPrice() * dto.getQty())
                                .setLot(dto.getLot())
                                .setProductionDate(dto.getProductionDate())
                                .setExpiredDate(dto.getExpiredDate())
                                .setSterilizationDate(dto.getSterilizationDate());
                        itemDTOS.add(item);
                    } else {
                        // throw new BusinessException("采购订单明细不存在!");
                        importResult.getList().remove(dto);
                        dto.setErrorMsg(String.format("采购订单明细不存在"));
                        importResult.getFailList().add(dto);
                        log.warn("第{}行采购订单明细不存在，请先确认数据是否正确；采购单号：{}", dto.getRowNum(), dto.getPoCode() + dto.getMaterialSpec());
                        continue;
                    }
                }

                BeanUtils.copyProperties(itemDTOS, items, DeliveryItem.class);
                //保存更新发货单明细
                if (items.size() > 0) {
                    deliveryItemService.saveOrUpdateBatchDtl(items);
                }
            }
        };

        BatchSaveTask<ExcelDeliveryDTO> task = new BatchSaveTask(saveHandler, ldtoList);

        this.forkJoinPool.invoke(task);

        //BeanUtils.copyProperties(itemDTOS, items, DeliveryItem.class);

        //保存更新发货单明细
       /* if (items.size() > 0) {
            deliveryItemService.saveOrUpdateBatchDtl((ArrayList<DeliveryItem>) items);
        }*/
        return importResult;
    }


}
